#include "ohos_video_encoder.h"
#include "rtc_base/logging.h"
#include "rtc_base/checks.h"
#include "modules/video_coding/include/video_codec_interface.h"
#include "modules/video_coding/include/video_error_codes.h"
#include "modules/video_coding/codecs/h264/include/h264.h"
#include "libyuv/convert.h"
#include <multimedia/player_framework/native_avcodec_videoencoder.h>
#include <multimedia/player_framework/native_avcapability.h>
#include <multimedia/player_framework/native_avformat.h>
#include "common_video/ohos/ohos_video_buffer.h"
#include <algorithm>

#define MAX_NALUS_PERFRAME 32
#define NAL_SC_LENGTH 4
static const int kLowH264QpThreshold = 1;
static const int kHighH264QpThreshold = 51;

namespace webrtc {

static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) {
    (void)codec;
    (void)errorCode;
    (void)userData;
    RTC_LOG_F(LS_ERROR) << "OhosVideoEncoder OnError, encoder: " << userData << " ,errorCode: " << errorCode;
}

static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
    (void)codec;
    (void)format;
    (void)userData;
    RTC_LOG_F(LS_INFO) << "OhosVideoEncoder OnStreamChanged, encoder: " << userData << " ,OH_AVFormat: " << format;
}

static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    auto encoder = static_cast<OhosVideoEncoder *>(userData);
    if (encoder) {
        encoder->OnCodecNeedInputData(index, buffer);
    }
}

static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
    auto encoder = static_cast<OhosVideoEncoder *>(userData);
    if (encoder) {
        encoder->OnCodecNewOutputData(index, buffer);
    }
}

OhosVideoEncoder::OhosVideoEncoder() {
  RTC_LOG(LS_INFO) << "OhosVideoEncoder::Ctor ";
}

OhosVideoEncoder::~OhosVideoEncoder() {
  RTC_LOG(LS_INFO) << "OhosVideoEncoder::dtor ";
  Release();
}

int32_t OhosVideoEncoder::InitEncode(const VideoCodec* codec_settings, const Settings& settings) {
    codec_setting_ = *codec_settings;
    if(codec_setting_.codecType != kVideoCodecH264 && codec_setting_.codecType != kVideoCodecH265) {
        RTC_LOG_T(LS_ERROR) << "codec OhosVideoEncoder only support h264 or h265";
        return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
    }
    min_qp_ = codec_setting_.minQp;
    max_qp_ = codec_setting_.maxQp;

    RTC_LOG_T(LS_INFO) << "OhosVideoEncoder::InitEncode. w:" << codec_setting_.width
                       << ", h:" << codec_setting_.height << ", mode:" << (int32_t)codec_setting_.mode
                       << ", type:" << codec_setting_.codecType
                       << ", startBitrate:" << codec_setting_.startBitrate
                       << " ,maxBitrate: " << codec_setting_.maxBitrate
                       << " ,minBitrate: " << codec_setting_.minBitrate
                       << " ,maxFramerate: " << codec_setting_.maxFramerate
                       << ", min_qp:"<<min_qp_
                       << ", max_qp:"<<max_qp_
                       << ", keyframeInterval:"<< codec_setting_.H264()->keyFrameInterval
                       << ", numofstream:" << codec_setting_.numberOfSimulcastStreams;

    return SetupEncoderInternal();
}

int32_t OhosVideoEncoder::Encode(const VideoFrame &input_image, const std::vector<VideoFrameType> *frame_types) {
  if (oh_video_encoder_ == nullptr) {
    return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
  }

  if (!active_) {
    return WEBRTC_VIDEO_CODEC_OK;
  }

  bool is_texture = input_image.is_texture();
  if (is_texture != use_surface_) {
    RTC_LOG_T(LS_INFO)<<"OhosVideoEncoder::Encode, different source attribute, frame is texture:"<<is_texture<<", use surface:"<<use_surface_;
    ReleaseEncoderInternal();
    use_surface_ = is_texture;
    int32_t ret = SetupEncoderInternal();
    if (ret != WEBRTC_VIDEO_CODEC_OK) {
      return ret;
    }
  }

  bool is_keyframe_required = false;
  if (frame_types) {
    for (auto frame_type : *frame_types) {
      if (frame_type == VideoFrameType::kVideoFrameKey) {
        is_keyframe_required = true;
        break;
      }
    }
  }

  if (is_keyframe_required) {
    EncodeKeyFrame();
  }

  int32_t ret;
  if (is_texture) {
    ret = EncodeTextureFrame(input_image);
  } else {
    ret = EncodeByteFrame(input_image);
  }

  if (ret != WEBRTC_VIDEO_CODEC_OK) {
    return ret;
  } else {
    std::lock_guard<std::mutex> lock(infos_mutex_);
    input_video_infos_.emplace(input_image.width(), input_image.height(), input_image.timestamp_us(), input_image.ntp_time_ms(), input_image.timestamp(), input_image.rotation());
  }

  return WEBRTC_VIDEO_CODEC_OK;
}

int32_t OhosVideoEncoder::RegisterEncodeCompleteCallback(EncodedImageCallback *callback) {
  callback_ = callback;
  return WEBRTC_VIDEO_CODEC_OK;
}

void OhosVideoEncoder::SetRates(const RateControlParameters& rc_parameters) {
  double new_framerate = rc_parameters.framerate_fps;
  if (new_framerate < 1.0f) {
    return;
  }

  new_framerate = std::max(5.0f, (float)new_framerate);

  if (oh_video_encoder_ == nullptr) {
    return;
  }

  if (rc_parameters.bitrate.get_sum_bps() == 0) {
    // Encoder paused, turn off all encoding.
    active_ = false;
    RTC_LOG_T(LS_INFO) << "OhosVideoEncoder update bitrate as 0, stop encoding";
    return;
  }

  RTC_DCHECK_GE(rc_parameters.bitrate.get_sum_kbps(), codec_setting_.minBitrate);
  if (codec_setting_.numberOfSimulcastStreams > 0) {
    RTC_DCHECK_GE(rc_parameters.bitrate.get_sum_kbps(), codec_setting_.simulcastStream[0].minBitrate);
  }

  codec_setting_.maxFramerate = static_cast<uint32_t>(new_framerate);
  uint32_t target_bitrate = rc_parameters.bitrate.GetSpatialLayerSum(0);
  if (target_bitrate) {
    RTC_LOG_T(LS_INFO) << "OhosVideoEncoder update bitrate:"<<target_bitrate<<", framerate:"<<new_framerate;
    active_ = true;
    OH_AVFormat *format = OH_AVFormat_Create();
    OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, target_bitrate);
    OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, new_framerate);
    int32_t ret = OH_VideoEncoder_SetParameter(oh_video_encoder_, format);
    if (ret != AV_ERR_OK) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder update bitrate failed: " << ret;
    }
    OH_AVFormat_Destroy(format);
  } else {
    active_ = false;
  }
}

VideoEncoder::EncoderInfo OhosVideoEncoder::GetEncoderInfo() const {
  VideoEncoder::EncoderInfo info;
  info.supports_native_handle = true;
  info.is_hardware_accelerated = true;
  info.implementation_name = codec_name_;
  info.scaling_settings = VideoEncoder::ScalingSettings(kLowH264QpThreshold, kHighH264QpThreshold);
  return info;
}

int32_t OhosVideoEncoder::Release() {
  RTC_LOG_T(LS_INFO) << "OhosVideoEncoder::Release";
  ReleaseEncoderInternal();
  active_ = true;
  return 0;
}

int32_t OhosVideoEncoder::EncodeByteFrame(const VideoFrame &input_image) {
  EncoderInputData input_data;
  {
    std::lock_guard<std::mutex> lock(queue_mutex_);
    if (input_data_queue_.empty()) {
        return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
    }

    input_data = input_data_queue_.front();
    input_data_queue_.pop();

    rtc::scoped_refptr<I420BufferInterface> buffer(input_image.video_frame_buffer()->ToI420());
    int32_t width = buffer->width();
    int32_t height = buffer->height();
    int32_t dstStrideY = encoder_stride_w_;
    int32_t dstStrideUV = dstStrideY / 2;
    uint8_t *dstY = reinterpret_cast<uint8_t *>(OH_AVBuffer_GetAddr(input_data.mem));
    uint8_t *dstU = dstY + (dstStrideY * height);
    uint8_t *dstV = dstU + (dstStrideUV * height / 2);
    libyuv::I420Copy(buffer->DataY(), buffer->StrideY(), buffer->DataU(), buffer->StrideU(),
        buffer->DataV(), buffer->StrideV(), dstY, dstStrideY, dstU, dstStrideUV,
        dstV, dstStrideUV, width, height);
    OH_AVCodecBufferAttr info;
    info.size = dstStrideY * height * 3/ 2;
    info.offset = 0;
    info.pts = input_image.timestamp_us();
    int32_t ret = OH_AVBuffer_SetBufferAttr(input_data.mem, &info);
    if (ret != AV_ERR_OK) {
        RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::Encode, set buffer attrib failed: " << ret;
        return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
    }

    ret = OH_VideoEncoder_PushInputBuffer(oh_video_encoder_, input_data.index);
    if (ret != AV_ERR_OK) {
        RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::Encode, push input buffer failed: " << ret;
        return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
    }
  }
  return WEBRTC_VIDEO_CODEC_OK;
}

int32_t OhosVideoEncoder::EncodeTextureFrame(const VideoFrame &input_image) {
  auto egl_resource = OhosEGLContextManager::GetInstance().GetEGLContextResource();
  if (!egl_resource) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::Encode, get EGL resource failed!";
    return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
  }
  OhosVideoBuffer *ohos_buffer = static_cast<OhosVideoBuffer*>(input_image.video_frame_buffer().get());
  std::lock_guard<std::mutex>(*(OhosEGLContextManager::GetInstance().GetTextureMutex(ohos_buffer->GetVideoFrameBuffer().textureID)));
  gl_drawer_->DrawFrame(ohos_buffer->GetVideoFrameBuffer(), matrix_, 0, 0, 0, 0, codec_setting_.width, codec_setting_.height);
  gl_context_->SwapBuffers(egl_surface_);
  return WEBRTC_VIDEO_CODEC_OK;
}

int32_t OhosVideoEncoder::EncodeKeyFrame() {
  RTC_LOG_T(LS_INFO) << "OhosVideoEncoder request key frame!";
  if (oh_video_encoder_ == nullptr) {
    RTC_LOG_T(LS_WARNING) << "OhosVideoEncoder request key frame, encode not started!";
    return -1;
  }
  OH_AVFormat *format = OH_AVFormat_Create();
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_REQUEST_I_FRAME, true);
  int32_t ret = OH_VideoEncoder_SetParameter(oh_video_encoder_, format);
  if (ret != AV_ERR_OK) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder request key frame failed: " << ret;
  }
  OH_AVFormat_Destroy(format);
  return ret;
}

void OhosVideoEncoder::OnCodecNeedInputData(uint32_t index, OH_AVBuffer *mem) {
  std::lock_guard<std::mutex> lock(queue_mutex_);
  input_data_queue_.push({index, mem});
}

void OhosVideoEncoder::OnCodecNewOutputData(uint32_t index, OH_AVBuffer *mem) {
  if (oh_video_encoder_ == nullptr) {
      return;
  }
  int32_t ret;
  OH_AVCodecBufferAttr buffer_info;
  ret = OH_AVBuffer_GetBufferAttr(mem, &buffer_info);
  if (ret != AV_ERR_OK) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder get buffer attrib failed: " << ret;
    OH_VideoEncoder_FreeOutputBuffer(oh_video_encoder_, index);
    return;
  }
  if (buffer_info.size <= 0) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder invalid encoded buffer!";
    OH_VideoEncoder_FreeOutputBuffer(oh_video_encoder_, index);
    return;
  }
  uint8_t * encoded_data = reinterpret_cast<uint8_t*>(OH_AVBuffer_GetAddr(mem));
  int32_t encoded_size = buffer_info.size;
  if (buffer_info.flags & AVCODEC_BUFFER_FLAGS_CODEC_DATA) {
    RTC_LOG_T(LS_INFO) << "OhosVideoEncoder codec specific data generated, size:"<<encoded_size;
    csd_data_.SetData(encoded_data, encoded_size);
  } else {
    rtc::BufferT<uint8_t> final_data;
    if (csd_data_.size() > 0 && buffer_info.flags & AVCODEC_BUFFER_FLAGS_SYNC_FRAME) {
      final_data.AppendData(csd_data_.data(), csd_data_.size());
    }
    final_data.AppendData(encoded_data, encoded_size);
    EncoderInputVideoInfo input_info(0, 0, 0, 0, 0, kVideoRotation_0);
    {
      std::lock_guard<std::mutex> lock(infos_mutex_);
      if (input_video_infos_.empty()) {
        RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder input info is empty!";
        OH_VideoEncoder_FreeOutputBuffer(oh_video_encoder_, index);
        return;
      }
      input_info = input_video_infos_.front();
      input_video_infos_.pop();
    }
    EncodedImage encodedFrame;
    encodedFrame.SetEncodedData(EncodedImageBuffer::Create(final_data.data(), final_data.size()));
    encodedFrame._encodedHeight = input_info.height;
    encodedFrame._encodedWidth = input_info.width;
    encodedFrame.capture_time_ms_ = input_info.timestamp_us/1000;
    encodedFrame.ntp_time_ms_ = input_info.ntp_timestamp_ms;
    encodedFrame.SetRtpTimestamp(input_info.rtp_timestamp);
    encodedFrame.rotation_ = input_info.rotation;
    if (buffer_info.flags & AVCODEC_BUFFER_FLAGS_SYNC_FRAME) {
      encodedFrame._frameType = VideoFrameType::kVideoFrameKey;
    } else {
      encodedFrame._frameType = VideoFrameType::kVideoFrameDelta;
    }

    CodecSpecificInfo info;
    memset(&info, 0, sizeof(info));
    info.codecType = codec_setting_.codecType;
    info.codecSpecific.H264.packetization_mode = H264PacketizationMode::NonInterleaved;
    info.codecSpecific.H264.temporal_idx = 0;
    info.codecSpecific.H264.base_layer_sync = false;
    info.codecSpecific.H264.idr_frame = (encodedFrame._frameType == VideoFrameType::kVideoFrameKey);
    h264_bitstream_parsers_.ParseBitstream(encodedFrame);
    encodedFrame.qp_ = h264_bitstream_parsers_.GetLastSliceQp().value_or(-1);
    if (callback_) {
      callback_->OnEncodedImage(encodedFrame, &info);
    }
  }
  ret = OH_VideoEncoder_FreeOutputBuffer(oh_video_encoder_, index);
  if (ret != AV_ERR_OK) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::OnNeedOutputData free output buffer failed: " << ret;
  }
}

bool OhosVideoEncoder::InitGLContext() {
  gl_context_ = std::make_unique<EGLRenderContext>();
  gl_drawer_ = std::make_unique<OhosGLDrawer>();
  int32_t ret = OH_VideoEncoder_GetSurface(oh_video_encoder_, &native_window_);
  if (ret != AV_ERR_OK) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitGLContext get surface failed, ret:" << ret;
    return false;
  }
  ret = OH_NativeWindow_NativeWindowHandleOpt(native_window_, SET_BUFFER_GEOMETRY, codec_setting_.width, codec_setting_.height);
  if (ret != AV_ERR_OK) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitGLContext set surface buffer failed, ret:" << ret;
    return false;
  }
  std::shared_ptr<EGLContextResource> egl_resource = OhosEGLContextManager::GetInstance().GetEGLContextResource();
  if (egl_resource == nullptr) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitGLContext get EGL resource failed!";
    return false;
  }
  if (!gl_context_->Init(egl_resource->eglContext_->GetEGLContext())) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitGLContext init EGL failed!";
    return false;
  }
  egl_surface_ = gl_context_->CreateEglSurface(static_cast<EGLNativeWindowType>(native_window_));
  if (egl_surface_ == EGL_NO_SURFACE) {
    RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitGLContext create EGLSurface failed!";
    return false;
  }
  gl_context_->MakeCurrent(egl_surface_);
  gl_drawer_->CreateGLResources();
  RTC_LOG_T(LS_INFO)<<"OhosVideoEncoder::InitGLContext success!";
  return true;
}

int32_t OhosVideoEncoder::SetupEncoderInternal() {
  RTC_LOG_T(LS_INFO)<<"OhosVideoEncoder::SetupEncoderInternal start";
  int32_t ret = AV_ERR_OK;
  // 1: create encode by mime type
  const char *mime = (codec_setting_.codecType == kVideoCodecH264) ? OH_AVCODEC_MIMETYPE_VIDEO_AVC : OH_AVCODEC_MIMETYPE_VIDEO_HEVC;
  OH_AVCapability *capability = OH_AVCodec_GetCapabilityByCategory(mime, true, HARDWARE);
  if (!capability) {
    RTC_LOG_T(LS_ERROR) << "codec OhosVideoEncoder Hardware Not Support";
    return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
  }
  if (!OH_AVCapability_IsVideoSizeSupported(capability, codec_setting_.width, codec_setting_.height)) {
    RTC_LOG_T(LS_ERROR) << "codec OhosVideoEncoder video size not support!";
    return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
  }
  codec_name_ = OH_AVCapability_GetName(capability);
  oh_video_encoder_ = OH_VideoEncoder_CreateByName(codec_name_.c_str());
  RTC_LOG_T(LS_INFO) << "OhosVideoEncoder::InitEncode OH_VideoEncoder_CreateByName: " << codec_name_.c_str();
  // 2: add encode configure format
  // must config: width,height,PIXELFORMAT
  OH_AVPixelFormat pixel_format = AV_PIXEL_FORMAT_YUVI420;
  OH_AVFormat *format = OH_AVFormat_Create();
  int32_t profile = static_cast<int32_t>(OH_AVCProfile::AVC_PROFILE_HIGH);
  int32_t rateMode = static_cast<int32_t>(OH_VideoEncodeBitrateMode::CBR);
  int32_t iFrameInterval = codec_setting_.H264()->keyFrameInterval > 0 ? codec_setting_.H264()->keyFrameInterval : 20000;
  int64_t bitRate = codec_setting_.startBitrate*1000;
  OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, codec_setting_.maxFramerate);
  // OH_AVFormat_SetIntValue(format, OH_MD_KEY_RANGE_FLAG, rangeFlag);
  // OH_AVFormat_SetIntValue(format, OH_MD_KEY_COLOR_PRIMARIES, primary);
  // OH_AVFormat_SetIntValue(format, OH_MD_KEY_TRANSFER_CHARACTERISTICS, transfer);
  // OH_AVFormat_SetIntValue(format, OH_MD_KEY_MATRIX_COEFFICIENTS, matrix);
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, iFrameInterval);
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE, profile);
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, rateMode);
  OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, bitRate);
  // OH_AVFormat_SetIntValue(format, OH_MD_KEY_QUALITY, quality);
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, codec_setting_.width);
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, codec_setting_.height);
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, pixel_format);
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_QP_MAX, max_qp_);
  OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODER_QP_MIN, min_qp_);
  ret = OH_VideoEncoder_Configure(oh_video_encoder_, format);
  OH_AVFormat_Destroy(format);
  if(ret != AV_ERR_OK) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitEncode OH_VideoEncoder_Configure failed: " << ret;
      return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
  }

  OH_AVCodecCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer};
  ret = OH_VideoEncoder_RegisterCallback(oh_video_encoder_, cb, this);
  if (ret != AV_ERR_OK) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitEncode OH_VideoEncoder_SetCallback failed: " << ret;
      return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
  }

  if (use_surface_) {
    if (!InitGLContext()) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitEncode init GL context failed!";
      return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
    }
  }

  ret = OH_VideoEncoder_Prepare(oh_video_encoder_);
  if (ret != AV_ERR_OK) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitEncode OH_VideoEncoder_Prepare failed: " << ret;
      return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
  }

  ret = OH_VideoEncoder_Start(oh_video_encoder_);
  if (ret != AV_ERR_OK) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitEncode OH_VideoEncoder_Start failed: " << ret;
      return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE;
  }
  OH_AVFormat *des_format = OH_VideoEncoder_GetInputDescription(oh_video_encoder_);
  OH_AVFormat_GetIntValue(des_format, OH_MD_KEY_VIDEO_STRIDE, &encoder_stride_w_);
  OH_AVFormat_GetIntValue(des_format, OH_MD_KEY_VIDEO_SLICE_HEIGHT, &encoder_stride_h_);
  OH_AVFormat_Destroy(des_format);
  RTC_LOG_T(LS_INFO)<<"OhosVideoEncoder, encoder stride_w:"<<encoder_stride_w_<<", stride_h:"<<encoder_stride_h_;
  RTC_LOG_T(LS_INFO)<<"OhosVideoEncoder::SetupEncoderInternal success!";
  return WEBRTC_VIDEO_CODEC_OK;
}

void OhosVideoEncoder::ReleaseEncoderInternal() {
  RTC_LOG_T(LS_INFO)<<"OhosVideoEncoder::ReleaseEncoderInternal start";
  if (oh_video_encoder_ == nullptr) {
      return;
  }
  int32_t ret = OH_VideoEncoder_Flush(oh_video_encoder_);
  ret = OH_VideoEncoder_Stop(oh_video_encoder_);
  if (ret != AV_ERR_OK) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitEncode OH_VideoEncoder_Stop failed: " << ret;
  }
  if (native_window_) {
    OH_NativeWindow_DestroyNativeWindow(native_window_);
    native_window_ = nullptr;
  }
  ret = OH_VideoEncoder_Destroy(oh_video_encoder_);
  if (ret != AV_ERR_OK) {
      RTC_LOG_T(LS_ERROR) << "OhosVideoEncoder::InitEncode OH_VideoEncoder_Destroy failed: " << ret;
  }
  oh_video_encoder_ = nullptr;

  if (gl_drawer_ != nullptr && gl_context_ != nullptr && egl_surface_ != EGL_NO_SURFACE) {
    gl_context_->MakeCurrent(egl_surface_);
    gl_drawer_.reset();
    gl_context_->MakeCurrent();
  }

  if (egl_surface_ != EGL_NO_SURFACE && gl_context_ != nullptr) {
    gl_context_->DestroyEglSurface(egl_surface_);
    egl_surface_ = EGL_NO_SURFACE;
  }

  gl_context_.reset();

  {
    std::lock_guard<std::mutex> lock(queue_mutex_);
    std::queue<EncoderInputData> empty;
    input_data_queue_.swap(empty);
  }

  {
    std::lock_guard<std::mutex> lock(infos_mutex_);
    std::queue<EncoderInputVideoInfo> empty;
    input_video_infos_.swap(empty);
  }

  csd_data_.Clear();
  RTC_LOG_T(LS_INFO)<<"OhosVideoEncoder::ReleaseEncoderInternal success!";
}

} // namespace webrtc